package handler

import (
	"crypto/sha512"
	"encoding/hex"
	"github.com/gin-gonic/gin"
	"goodcenter/internal/model"
	"goodcenter/utils/resx"
	"io"
	"net/http"
	"os"
	"strconv"
	"strings"
)

// 全体操作

func GoodList(ctx *gin.Context) {
	stat := ctx.DefaultQuery("stat", "online")
	part := ctx.DefaultQuery("part", "1")
	if part == "0" {
		goods := model.QueryGoods(stat, part, 0, 0)
		ctx.JSON(200, resx.GinResp(resx.Success, resx.DataList{
			Items: goods,
			Total: len(goods),
		}))
		return
	}

	page := ctx.DefaultQuery("page", "1")
	pmax := ctx.DefaultQuery("pmax", "6")

	pageInt, err1 := strconv.Atoi(page)
	pmaxInt, err2 := strconv.Atoi(pmax)
	if err1 != nil || err2 != nil {
		ctx.JSON(200, resx.GinResp(resx.BadRequest, nil))
		return
	}
	offset := (pageInt - 1) * pmaxInt
	count, err := model.QueryGoodCount(stat)
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}
	if count <= int64(offset) {
		if count == 0 {
			ctx.JSON(200, resx.GinResp(resx.OutOfRange, &gin.H{"last": 0}))
			return
		}
		ctx.JSON(200, resx.GinResp(resx.OutOfRange, &gin.H{"last": count/int64(pmaxInt) + 1}))
		return
	}

	goods := model.QueryGoods(stat, part, offset, pmaxInt)
	ctx.JSON(200, resx.GinResp(resx.Success, &resx.DataList{
		Items:    goods,
		Total:    len(goods),
		LastPage: count/int64(pmaxInt) + 1,
	}))
	return
}

func GoodDetail(ctx *gin.Context) {
	goodId := ctx.Param("id")
	if goodId == "" {
		ctx.JSON(200, resx.GinResp(resx.BadRequest, nil))
		return
	}
	goodInfo := model.QueryGoodById(goodId)
	if goodInfo == nil {
		ctx.JSON(200, resx.GinResp(resx.NilResult, nil))
		return
	}

	ctx.JSON(200, resx.GinResp(resx.Success, goodInfo))
	return
}

// 管理员操作

func GoodInsert(ctx *gin.Context) {
	newGood := GoodMsgRequest{}
	if err := ctx.BindJSON(&newGood); err != nil {
		ctx.JSON(200, resx.GinResp(resx.BadRequest, nil))
		return
	}
	if oldGood := model.QueryGoodByName(newGood.Name); oldGood != nil && oldGood.State != "" {
		ctx.JSON(200, resx.GinResp(resx.BeOccupied, nil))
		return
	}
	err := model.InsertGood(&model.Good{
		Name:    newGood.Name,
		Des:     newGood.Des,
		Picture: newGood.PictureHash,
		State:   newGood.State,
	})
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	ctx.JSON(200, resx.GinResp(resx.Success, nil))
	return
}

func GoodUpdate(ctx *gin.Context) {
	goodId := ctx.Param("id")
	goodIdInt64, turnErr := strconv.Atoi(goodId)
	if goodId == "" || turnErr != nil {
		ctx.JSON(200, resx.GinResp(resx.BadRequest, nil))
		return
	}

	newGood := GoodMsgRequest{}
	if err := ctx.ShouldBindJSON(&newGood); err != nil {
		ctx.JSON(200, resx.GinResp(resx.BadRequest, nil))
		return
	}

	if oldGood := model.QueryGoodById(goodId); oldGood.Name != newGood.Name {
		if sameGood := model.QueryGoodByName(newGood.Name); sameGood != nil && sameGood.State != "" {
			ctx.JSON(200, resx.GinResp(resx.BeOccupied, "该商品名已被使用"))
			return
		}
	}
	err := model.UpdateGood(&model.Good{
		GoodId:  int64(goodIdInt64),
		Name:    newGood.Name,
		Des:     newGood.Des,
		Picture: newGood.PictureHash,
		State:   newGood.State,
	})
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	ctx.JSON(200, resx.GinResp(resx.Success, nil))
	return
}

func GoodDelete(ctx *gin.Context) {
	goodId := ctx.Param("id")
	if goodId == "" {
		ctx.JSON(200, resx.GinResp(resx.BadRequest, nil))
		return
	}

	err := model.DeleteGood(goodId)
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.Fail, nil))
		return
	}

	ctx.JSON(200, resx.GinResp(resx.Success, nil))
	return
}

func Upload(ctx *gin.Context) {
	fileHeader, err := ctx.FormFile("upload")
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.BadRequest, nil))
		return
	}

	// 大小限制
	if fileHeader.Size > 1024*1024*10 {
		ctx.JSON(200, resx.GinResp(resx.SizeLimitExceeded, nil))
		return
	}

	// 文件类型判断以及计算哈希值
	file, err := fileHeader.Open()
	defer file.Close()
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	var data []byte
	buf := make([]byte, 10240)
	for {
		n, err := file.Read(buf)
		if err != nil && err != io.EOF {
			ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
			return
		}
		if n == 0 {
			break
		}
		data = append(data, buf[:n]...)
	}
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	bytes := sha512.Sum512(data)
	hex := hex.EncodeToString(bytes[:])
	path := "./static/image/" + hex

	if _, err := os.Stat(path); err != nil {
		fileType := http.DetectContentType(data[0:512])
		types := strings.Split(fileType, "/")
		if types[0] != "image" {
			ctx.JSON(200, resx.GinResp(resx.TypeNotAllowed, nil))
			return
		}
		saveErr := ctx.SaveUploadedFile(fileHeader, path)
		if saveErr != nil {
			ctx.JSON(200, resx.GinResp(resx.SystemErr, saveErr))
			return
		}
	}

	ctx.JSON(200, resx.GinResp(resx.Success, hex))
}

type GoodMsgRequest struct {
	Name        string `json:"name" binding:"required"`
	Des         string `json:"des" binding:"required"`
	PictureHash string `json:"pictureHash" binding:"required"`
	State       string `json:"state" binding:"required"`
}
